#! /usr/bin/env python3

import sys
import subprocess
import argparse
import re
from enum import Enum

class Option:
	def __init__( self_, short_, long_, value_, desc_, optional_ ):
		self_._short = short_
		self_._long = long_
		self_._value = value_
		self_._desc = desc_
		self_._optional = optional_

def prepare_description( str_ ):
	capitalized = str_[0].upper() + str_[1:]
	parts = capitalized.split( "`" )
	for i in range( len( parts ) ):
		if i % 2 == 1:
			parts[i] = parts[i].replace( "_", "**" ).replace( "\\**", "\\_" )
	return "_".join( parts )

class Style( Enum ):
	STRONG = 1
	EMPHASIS = 2
	CODE = 3

def markdown_to_asciidoc( str_ ):
	str_ = str_.strip()
	styles = []
	special = "*`$_"
	escape = False
	marker = False
	s = ""

	def apply( style_, mark_ ):
		nonlocal s, styles, marker
		markers = { Style.STRONG: "**", Style.EMPHASIS: "__" }
		marker = False
		if styles and styles[-1] == style_:
			styles.pop()
			s += mark_
			if styles:
				s += markers[styles[-1]]
		else:
			if styles:
				s += markers[styles[-1]]
			s += mark_
			styles.append( style_ )

	for c in str_:
		if escape:
			if c in special:
				s += "+++{}+++".format( c )
			else:
				s += "\\{}".format( c )
			escape = False
			continue
		if c in "*_":
			if marker:
				apply( Style.STRONG, "**" )
			else:
				marker = True
			continue
		if marker:
			apply( Style.EMPHASIS, "__" )
		if c == '\\':
			escape = True
			continue

		s += c

	return s

testStr = "Some **text** with *markdown* and specials like: ^#!.\\*huginn.\\* - not so easy.\n"

def words( str_ ):
	pattern = re.compile( "([a-zA-Z]+)" )
	m = pattern.search( str_ )
	words = []
	if m:
		words = map( lambda x: x.lower(), m.groups() )
	return words

def find_urls( path_ ):
	try:
		with open( path_, "rb" ) as f:
			data = f.read()
	except:
		return []
	urls = []
	size = len( data )
	start = 0
	while start >= 0:
		lastStart = start
		start = data.find( b"http", start )
		if start >= 0:
			bck = start
			while ( bck > lastStart ) and ( data[bck] in range( 32, 128 ) ):
				bck -= 1
			context = words( data[bck:start].decode() )
			fwd = start
			while ( fwd < size ) and ( data[fwd] in range( 32, 128 ) ):
				fwd += 1
			urls.append( ( context, data[start:fwd].decode() ) )
			start = fwd
	return urls

def url_info( url_ ):
	wordsOfInterest = [ "homepage", "website", "home", "resource", "resources", "page" ]
	urlInfo = url_[1]
	for woi in wordsOfInterest:
		if woi in url_[0]:
			urlInfo = "{}: {}".format( woi.capitalize(), url_[1] )
	return urlInfo

def local():
	try:
		return subprocess.check_output(["src/gen-asciidoc"]).decode()
	except:
		pass
	return ""

def main():
#	print( "{}{}".format( testStr, markdown_to_asciidoc( testStr ) ) )
#	return ( 0 )
	parser = argparse.ArgumentParser( description = "Generate asciidoc file based on output of `{prj} --help -v' call." )
	parser.add_argument( "-p", "--project", metavar = "name", type = str, required = True, help = "Name of the project to generate asciidoc for." )
	parser.add_argument( "-o", "--output", metavar = "path", type = str, required = True, help = "Destination path for result asciidoc file." )
	args = parser.parse_args()
	binary = "./build/release/" + args.project + "/1exec"
	help = subprocess.Popen( [ binary, "--help", "-v" ], universal_newlines = True, stdout = subprocess.PIPE ).stdout.read()
	lines = help.split( "\n" )
	preamble = []
	options = []
	aliases = []
	footer = ""
	isPreamble = True
	isOptions = False
	for l in lines:
		bigIndent = l.startswith( " " * 9 )
		line = l.strip()
		if line == "Options:":
			continue
		optionStart = not bigIndent and line.startswith( "-" ) and not line.startswith( "- " )
		if optionStart:
			isPreamble = False
			isOptions = True
		elif isOptions and not l.startswith( " " ):
			isOptions = False
		if isPreamble:
			preamble.append( l )
		elif isOptions:
			if optionStart:
				words = line.split( " " )
				short = None
				longFrom = None
				value = None
				desc = ""
				for w in words:
					if len( desc ) == 0 and w.startswith( "--" ):
						longFrom = w.strip( "," )
					elif len( desc ) == 0 and w.startswith( "-" ):
						short = w.strip( "," )
					elif longFrom or value:
						desc += ( " " + w )
					else:
						value = w
				optional = False
				ss = short.split( "=" ) if short else None
				ls = longFrom.split( "=" ) if longFrom else None
				if ss and len( ss ) > 1:
					short = ss[0]
					value = ss[1]
				elif ls and len( ls ) > 1:
					longFrom = ls[0]
					value = ls[1]
				if value and value.endswith( "]" ):
					value = value.strip( "]" )
					optional = True
				desc = desc.strip()
				if desc.lower().find( "an alias for **" ) >= 0:
					desc = map( lambda x: x.strip( "*" ), desc[15:].split( ", " ) )
					aliases.append( ( short, longFrom ) + tuple( desc ) )
				elif len( desc ) > 0:
					options.append( Option( short, longFrom, value, desc.strip(), optional ) )
				else:
					o = options[-1]
					if short:
						if o._short:
							o._short += ( ", " + short )
						else:
							o._short = short
					if longFrom:
						if o._long:
							o._long += ( ", " + longFrom )
						else:
							o._long = longFrom
			else:
				options[-1]._desc += ( " " + line )
		else:
			footer += ( l + "\n" )
	for a in aliases:
		for o in options:
			if a[2] == o._short or ( ( len( a ) > 3 ) and ( a[3] == o._long ) ):
				if a[0]:
					o._short += ( ", " + a[0] )
				if a[1]:
					o._long += ( ", " + a[1] )
	usage = preamble[0].replace( binary, "**" + args.project + "**" )
	del preamble[0:2]
	name = preamble[0].replace( binary, args.project )
	del preamble[0:2]
	if usage.startswith( "Usage: " ):
		usage = usage[7:]

	out = open( args.output, "w" )
	out.write( args.project.upper() + "(1)\n" )
	out.write( "=" * ( len( args.project ) + 3 ) + "\n\n" )

	out.write( "NAME\n----\n" )
	out.write( name + "\n\n" )

	out.write( "SYNOPSIS\n--------\n" )

	opts = ""
	optsLen = 0
	maxLen = 70 - len( args.project )
	for o in options:
		if o._short:
			short = o._short.split( "," )[0]
			valName = " "
			if o._value:
				valName = " *{}* ".format( o._value )
				optsLen -= 2
			opt = "[ **{}**{}] ".format( short, valName )
			optsLen -= 4
			opts += opt
			optsLen += len( opt )
			if optsLen > maxLen:
				optsLen = 0
				opts += " +\n**" + " " * ( len( args.project ) ) + "** "
	usage = usage.replace( "[*OPTION*]... ", opts )

	out.write( markdown_to_asciidoc( usage ) + "\n\n" )

	out.write( "DESCRIPTION\n-----------\n" )
	out.write( "\n".join( map( markdown_to_asciidoc, preamble[:-1] ) ) + "\n\n" )

	if len( options ) > 0:
		out.write( "OPTIONS\n-------\n" )
	for o in options:
		out.write(
			"*{}{}{}*{}{}::\n".format(
				o._short if o._short else "",
				", " if o._short and o._long else "",
				o._long if o._long else "",
				"=" if o._long and o._value else " ",
				"'" + o._value + "'" + ( "*]*" if o._optional else "" ) if o._value else ""
			)
		)
		out.write( "\t" + markdown_to_asciidoc( prepare_description( o._desc ) ) + ( "." if not o._desc.endswith( "." ) else "" ) + "\n\n" )

	if len( footer ) > 1:
		out.write( markdown_to_asciidoc( footer.replace( binary, args.project ) ) + "\n\n" )

	out.write( local() )

	out.write( "AUTHORS\n-------\n" )
	authors = set( filter( lambda x : x, subprocess.check_output( [ "git", "log", "--format=%aN <%aE>" ], universal_newlines = True ).split( "\n" ) ) )
	out.write( "$$" + ", ".join( list( authors ) ) + "$$\n\n" )

	out.write( "RESOURCES\n---------\n" )
	for url in find_urls( binary ):
		out.write( "$${}$$ +\n".format( url_info( url ) ) )

	remote = ""

	urls = []
	for f in [ "_deploy/debian/control", "_deploy/centos/{}.spec".format( args.project ) ]:
		urls.extend( find_urls( f ) )

	for url in urls:
		if url[1].endswith( args.project + ".git" ):
			remote = url[1]
			break

	if not remote:
		remotesRaw = subprocess.check_output( [ "git", "remote", "-v" ], universal_newlines = True )
		for r in remotesRaw.split( "\n" ):
			w = r.split()
			if w[0] == "origin":
				remote = w[1]
				break

	out.write( "git repository: $$" + remote + "$$\n" )

if __name__ == "__main__":
	try:
		main()
	except Exception as e:
		print( e )
		sys.exit( 1 )

